package com.haohuo.exporter;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haohuo.config.excel.ExExcelExportService;
import com.haohuo.config.interfaces.ExportTask;
import com.haohuo.config.interfaces.IAppSettingService;
import com.haohuo.utils.ComResult;
import com.haohuo.utils.CptUtils;
import com.haohuo.utils.DateUtil;
import com.haohuo.utils.RetCode;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.haohuo.utils.ComResult.success;

public abstract class AbstractExportService<
        S extends ServiceImpl<? extends BaseMapper<E>, E>,
        O extends OSS,
        A extends IAppSettingService,
        E extends ExportTask> implements IExportService {
    private static final Logger logger = LoggerFactory.getLogger(AbstractExportService.class);

    private String basepath = System.getProperty("user.dir");
    @Autowired
    protected S isysDowntaskService;
    @Autowired
    protected A appSettings;
    @Autowired
    protected O ossClient;

    /**
     * 需要任务接口具体实现类，用来创建对象。
     * @return
     */
    protected abstract Class<E> getClazz() throws Exception;

    /**
     * BeanMap,ExcelExportEntity 需要子类返回
     *
     * @param obj
     * @return
     */
    protected abstract Map<String, List> getData(JSONObject obj) throws Exception;


    protected E modifyAndCheckObj(JSONObject obj) throws Exception {

        /** 校验参数完整性 **/
        Integer dtType = obj.getInteger("dtType");
        Integer optId = obj.getInteger("optId");

        //Integer userId = obj.getInteger("userId");

        Integer loginid = obj.getInteger("hh-loginid");
        Integer companyId = obj.getInteger("hh-companyId");
        String dtDes = obj.getString("dtDes");


        if (dtType == null || optId == null || loginid == null || companyId == null)
            throw new IllegalArgumentException(RetCode.RetNullPara_msg);

        Class<E> clazz = getClazz();
        E exportTask = clazz.newInstance();

        /* _12302_2021/7/2_< 此处其实就是子类，不过需要通过编译 > */
        return (E)exportTask
                .setDtType(dtType)
                .setDtState(1)
                .setDtDes(dtDes == null ? "haohuo" : dtDes)
                // maybe you need override
                .setDtParm(obj.toJSONString())
                .setDtUserId(companyId)
                .setDtOptId(loginid);

        // anything else ?
        // can super().. then .update object, or invalidation
    }



    @Override
    public ComResult addTask(JSONObject obj) {

        E sysTask;
        try {
            sysTask = modifyAndCheckObj(obj);
        } catch (Exception e) { return ComResult.error(-99, e.getMessage()); }

        List<E> a = isysDowntaskService.list(new QueryWrapper<E>().setEntity(sysTask));

        if (a.size() > 0)
            return success(new JSONObject() {{ put("flag", -2); }});

        sysTask.setDtStartTime(DateUtil.getNowTimeStr()).setDtParm(obj.toJSONString());
        isysDowntaskService.save(sysTask);

        return success(new JSONObject() {{ put("flag", sysTask.getDtId()); }});
    }

    /**
     * 处理下载任务
     */
    @Override
    @Async
    public ComResult excuteTask(int dtId) {
        E sysDowntask1 = isysDowntaskService.getById(dtId);
        if (sysDowntask1 != null && sysDowntask1.getDtId() > 0 && sysDowntask1.getDtState() == 1) {
            JSONObject params = JSONObject.parseObject(sysDowntask1.getDtParm());
            /** 处理业务逻辑 **/
            dealBusiness(params, sysDowntask1.getDtId());
        } else {
            sysDowntask1.setDtEndTime(DateUtil.getNowTimeStr()).setDtState(2);
            isysDowntaskService.updateById(sysDowntask1);
            logger.error("导入任务" + sysDowntask1.getDtId() + "不存在,或已经处理");
        }
        return success();
    }


    /**
     * 导出业务逻辑
     *
     * @param obj
     * @param dtId
     */
    protected void dealBusiness(JSONObject obj, Integer dtId) {

        /** 从子类获得list **/
        List<Map> list;
        JSONObject params;
        List<ExcelExportEntity> excelExportEntities = null;
        try {
            Map listsMap = getData(obj);
            list = (List<Map>) listsMap.get("beanMap");
            excelExportEntities = (List<ExcelExportEntity>) listsMap.get("excelExportEntity");
            params = JSONObject.class.cast(listsMap.get("params"));
        } catch (Exception e) {
            E sysTask = isysDowntaskService.getById(dtId);
            sysTask.setDtEndTime(DateUtil.getNowTimeStr()).setDtState(2);
            isysDowntaskService.updateById(sysTask);
            logger.error("由于【{}】无法从子类中获取ExcelExportEntity和beanMap", e.getMessage());
            return;
        }
        if (list == null || list.size() == 0 || excelExportEntities == null) {
            /** 更新任务表 **/
            E sysTask = isysDowntaskService.getById(dtId);
            sysTask.setDtEndTime(DateUtil.getNowTimeStr()).setDtState(2);
            isysDowntaskService.updateById(sysTask);
            logger.warn("下载任务数据为空");
            return;
        }

        /** 执行导出 **/
        String fileName = obj.getString("fileName");
        if (fileName == null) {fileName = "导出记录";}
        Workbook workbook = exportExcel(
                params != null ? transferParams(params) : new ExportParams(),
                excelExportEntities, list ,false);
        upload2OSS(workbook, dtId, fileName);
    }

    private static Workbook getWorkbook(ExcelType type, int size) {
        if (ExcelType.HSSF.equals(type)) {
            return new HSSFWorkbook();
        } else if (size < 100000) {
            return new XSSFWorkbook();
        } else {
            return new SXSSFWorkbook();
        }
    }

    private static Workbook exportExcel(ExportParams entity, List<ExcelExportEntity> entityList,
                                        Collection<?> dataSet, boolean isEx) {
        Workbook workbook = getWorkbook(entity.getType(),dataSet.size());
        ExcelExportService temp = isEx ? new ExExcelExportService() : new ExcelExportService();
        temp.createSheetForMap(workbook, entity, entityList, dataSet);
        return workbook;
    }

    protected static List<ExcelExportEntity> buildExportCustomEntity(Map<String, JSONObject> mapping, List<?> dataSet){
        return CptUtils.buildExportCustomEntity(mapping, dataSet);
    }

    private static ExportParams transferParams(JSONObject object){

        ExportParams exportParams = new ExportParams();
        List<String> fields = Arrays.stream(ExportParams.class.getDeclaredFields())
                .map(field -> field.getName()).collect(Collectors.toList());

        object.entrySet().stream()
                .filter(entry-> fields.contains(entry.getKey()))
                .forEach(entry->{
                    Field field;
                    try{
                        field = ExportParams.class.getDeclaredField(entry.getKey());
                        field.setAccessible(true);
                        field.set(exportParams,entry.getValue());
                    }catch (NoSuchFieldException | IllegalAccessException e) { /* _12302_2021/7/28_< ignore... > */ }
                });

        return exportParams;
    }


    protected void upload2OSS(Workbook workbook, Integer dtId, String fileName) {
        //文件名
        fileName = DateUtil.getNowTimeStr(DateUtil.FORMAT_DEFAULT_yyyyMMddHHmmss) +
                "_" + fileName + "_" + dtId + ".xls";

        StringBuilder ossfile = new StringBuilder()
                .append("export_cvs/")
                .append(DateUtil.getToday())
                .append("/")
                .append(fileName);

        FileOutputStream out = null;
        try {
            out = new FileOutputStream(basepath + "/" + fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            workbook.write(out);
            ossClient.putObject(appSettings.getOssImageBucket(), ossfile.toString(), new File(basepath + "/" + fileName));

            /** 更新任务表 **/
            E sysTask = isysDowntaskService.getById(dtId);
            sysTask.setDtEndTime(DateUtil.getNowTimeStr()).setDtOssurl(ossfile.toString()).setDtState(2);
            isysDowntaskService.updateById(sysTask);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关流
                out.close();
                workbook.close();
                //删除本地文件
                File file = new File(basepath + "/" + fileName);
                file.delete();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
